#include <ani.h>
#include <array>
#include <iostream>
#include <vector>

std::string ANIUtils_ANIStringToStdString(ani_env *env, ani_string ani_str)
{
    ani_size strSize;
    env->String_GetUTF8Size(ani_str, &strSize);

    std::vector<char> buffer(strSize + 1); // +1 for null terminator
    char *utf8_buffer = buffer.data();

    // String_GetUTF8 Supportted by https://gitee.com/openharmony/arkcompiler_runtime_core/pulls/3416
    ani_size bytes_written = 0;
    env->String_GetUTF8(ani_str, utf8_buffer, strSize + 1, &bytes_written);

    utf8_buffer[bytes_written] = '\0';
    std::string content = std::string(utf8_buffer);
    return content;
}

template<typename T>
void compareAndReport(const T& param1, const T& param2, const std::string& errorMessage) {
    if (param1 != param2) {
        std::cerr << errorMessage << std::endl;
        return;
    }
    // 如果需要处理相等的情况，可以在这里添加相应的逻辑
}

template<typename T>
void compareAndReport(const T& param1, const T& param2, const std::string& errorMessage, const std::string& message) {
    if (param1 != param2) {
        std::cerr << errorMessage << std::endl;
        return;
    }
    else {
        std::cerr << message << std::endl;
        return;
    }
}

ani_boolean handleData(ani_env *env, ani_object value)
{

    {
        ani_boolean result_obj = ANI_FALSE;
        ani_class errCls;
        // 可以查找更多Error类型
        const char* className = "escompat.Error";
        if (ANI_OK != env->FindClass(className, &errCls)) {
            std::cerr << "Not found '"  << className << std::endl;
            return result_obj;
        }
        
        ani_object errObj;
        // 查找构造函数，如果没有重载，可以直接传递 nullptr
        // 有构造函数重载的情况下，请准确根据需要的函数，传入函数签名的Mangling。
        ani_method errCtor;
        if (ANI_OK != env->Class_FindMethod(errCls, "<ctor>", "C{std.core.String}C{escompat.ErrorOptions}:", &errCtor)) {
            std::cerr << "get errCtor Failed'" << className << "'" << std::endl;
            return result_obj;
        }
        
        std::string name = "This will show message!";
        ani_string result_string{};
        env->String_NewUTF8(name.c_str(), name.size(), &result_string);
        
        //创建一个 Error  的实例
        if (ANI_OK != env->Object_New(errCls, errCtor, &errObj, result_string)) {
        std::cerr << "Create Object Failed'" << className << "'" << std::endl;
        return result_obj;
        }
        env->ThrowError(static_cast<ani_error>(errObj));
    }
    {
        ani_boolean result_obj = ANI_FALSE;
        ani_class errCls;
        if (ANI_OK != env->FindClass("std.core.NullPointerError", &errCls)) {
            std::cerr << "Not found '" << "std.core.NullPointerError" << "'" << std::endl;
            return result_obj;
        }

        //查找构造函数，如果没有重载，可以直接传递 nullptr (仅建议测试下使用)
        ani_method errCtor;
        if (ANI_OK != env->Class_FindMethod(errCls, "<ctor>", nullptr, &errCtor)){
            std::cerr << "get errCtor Failed'" << "std.core.NullPointerError" << "'" << std::endl;
            return result_obj;
        }

        ani_object errObj;
        //创建一个 Error  的实例
        if (ANI_OK != env->Object_New(errCls, errCtor, &errObj)){
        std::cerr << "Create Object Failed'" << "std.core.NullPointerError" << "'" << std::endl;
        return result_obj;
        }
        env->ThrowError(static_cast<ani_error>(errObj));
    }
    {
        ani_class cls;
        env->FindClass("escompat.Error", &cls);
        ani_method ctor;
        // escompat.Error的其中一个构造函数constructor(message?: String, options?: ErrorOptions)
        // 根据函数签名Manglin会找到不同的构造函数。
        env->Class_FindMethod(cls, "<ctor>", "C{std.core.String}C{std.core.ErrorOptions}:", &ctor);
        ani_object error;
        env->Object_New(cls, ctor, &error); //可以增加参数，第一个参数是message，此处没有添加。
        env->ThrowError(static_cast<ani_error>(error));
    }


    ani_class cls;
    env->FindClass("escompat.Error", &cls);
    
    ani_type typeError = cls;
    ani_boolean result;
    
    env->Object_InstanceOf(value, typeError, &result);
    return result;
}

ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;

    if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env))
    {
        std::cerr << "Unsupported ANI_VERSION_1" << std::endl;
        return ANI_ERROR;
    }

    static const char *moduleName = "ani_error";

    ani_module module;
    if (ANI_OK != env->FindModule(moduleName, &module))
    {
        std::cerr << "Not found '" << moduleName << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array methods = {
        ani_native_function{"handleData", "C{escompat.Error}:z", reinterpret_cast<void *>(handleData)},
    };

    std::cout << "Start bind native methods to '" << moduleName << "'" << std::endl;

    if (ANI_OK != env->Module_BindNativeFunctions(module, methods.data(), methods.size()))
    {
        std::cerr << "Cannot bind native methods to '" << moduleName << "'" << std::endl;
        return ANI_ERROR;
    };
    std::cout << "Finish bind native methods to '" << moduleName << "'" << std::endl;

    *result = ANI_VERSION_1;
    return ANI_OK;
}

/**
Start bind native methods to 'Lani_error;'
Finish bind native methods to 'Lani_error;'
Not found 'Lstd/core/NullPointerError;'
[TID 3e0c3d] E/runtime: Unhandled exception: escompat.Error
[TID 3e0c3d] E/runtime: Error: This will show message!
[TID 3e0c3d] E/runtime:         at ani_error.ETSGLOBAL.handleData (/mnt/data/l30052105/ani_cookbook/ani_error/ani_error.ets:-2)
[TID 3e0c3d] E/runtime:         at ani_error.ETSGLOBAL.main (/mnt/data/l30052105/ani_cookbook/ani_error/ani_error.ets:8)
[TID 3e0c3d] E/runtime: 
ninja: build stopped: subcommand failed.
 */